package run.bottle.securityauth.configuration;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.access.intercept.RequestAuthorizationContext;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import run.bottle.securityauth.access.AccessDecisionVoterAuthorizationManagerAdapter;
import run.bottle.securityauth.converter.CustomJwtAuthenticationConverter;
import run.bottle.urp.service.PermissionService;
import run.bottle.urp.service.UserService;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * OAuth resource configuration.
 *
 * @author liyc
 * @date 2022-03-06
 */
@EnableWebSecurity
public class OAuth2ResourceServerSecurityConfiguration {

    private final UserService userService;
    private final PermissionService permissionService;

    public OAuth2ResourceServerSecurityConfiguration(UserService userService, PermissionService permissionService) {
        this.userService = userService;
        this.permissionService = permissionService;
    }

    @Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
    String jwkSetUri;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http, AuthorizationManager<RequestAuthorizationContext> accessAuthorizationManager) throws Exception {
        // @formatter:off

        // cors跨域请求
        http.cors().configurationSource(corsConfigurationSource());

        // 1.授权码模式和客户端模式,这两种模式只要SCOPE验证通过则通过，这里写了两个固定的ant
        // 2.密码模式，通过用户角色所拥有的资源验证进行验证
        http
                .authorizeHttpRequests(authorization ->
                        authorization
                                // 以下三个是测试接口
                                .antMatchers(HttpMethod.GET,"/").permitAll()
                                .antMatchers(HttpMethod.POST,"/public/auth/login","/public/auth/logout").permitAll()
                                .antMatchers(HttpMethod.GET, "/message/**").hasAuthority("SCOPE_message.read")
                                .antMatchers(HttpMethod.POST, "/message/**").hasAuthority("SCOPE_message.write")
                                .anyRequest().access(accessAuthorizationManager)
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                        .jwt()
                        .jwtAuthenticationConverter(jwtAuthenticationConverter(userService))
                )
                .csrf().disable();

        // @formatter:on
        return http.build();
    }

    @Bean
    AuthorizationManager<RequestAuthorizationContext> accessAuthorizationManager() {
        return new AccessDecisionVoterAuthorizationManagerAdapter(permissionService);
    }

    /**
     * 构建jwt转换器
     * @param userService 提供用户信息查询服务
     * @return JwtAuthenticationConverter
     */
    public JwtAuthenticationConverter jwtAuthenticationConverter(UserService userService) {
        CustomJwtAuthenticationConverter grantedAuthoritiesConverter = new CustomJwtAuthenticationConverter(userService);

        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
        return jwtAuthenticationConverter;
    }

    /**
     * jwt解码器
     */
    @Bean
    public JwtDecoder jwtDecoder() {
        return NimbusJwtDecoder.withJwkSetUri(this.jwkSetUri).build();
    }

    /**
     * cors配置
     * @return CorsConfigurationSource
     */
    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Collections.singletonList("http://127.0.0.1:8080/"));
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        configuration.setAllowedHeaders(Collections.singletonList("*"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}
